feat: Add opt-in non-exclusive session locking for session receivers - #60060
Merged
Eldert Grootenboer (EldertGrootenboer) merged 29 commits intoAug 14, 2026
Merged
Eldert Grootenboer (EldertGrootenboer) merged 29 commits into
Eldert Grootenboer (EldertGrootenboer) merged 29 commits into
Conversation
Adds support for non-exclusive session locks, allowing a session to be cooperatively taken over by another receiver. - ServiceBusSessionReceiverOptions.IsSessionExclusive (default true) opts into non-exclusive locking via the AMQP source filter com.microsoft:session-exclusive-mode. - The service assigns a com.microsoft:session-lock-token (uuid), surfaced read-only as ServiceBusSessionReceiver.SessionLockToken. A second receiver presents the token via ServiceBusSessionReceiverOptions.SessionLockToken to take over the session and settle the original holder's messages over the management link. - Fails loudly with NotSupportedException against a service that does not support the feature; exclusive sessions are unaffected (default path).
Copilot started reviewing on behalf of
Eldert Grootenboer (EldertGrootenboer)
June 18, 2026 23:12
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an opt-in “non-exclusive” session locking mode to Azure.Messaging.ServiceBus session receivers, enabling cooperative session takeovers via AMQP source filters and a service-assigned session lock token, while keeping the default exclusive-lock behavior unchanged.
Changes:
- Introduces new public APIs:
ServiceBusSessionReceiver.SessionLockToken,ServiceBusSessionReceiverOptions.IsSessionExclusive, andServiceBusSessionReceiverOptions.SessionLockToken, plus a changelog entry and updated API listings. - Threads the new session exclusivity/token options through receiver construction into the transport layer, wiring AMQP source filters and surfacing the service-assigned token.
- Updates AMQP settlement behavior for session receivers to allow cross-receiver settlement paths for non-exclusive sessions; adds unit and gated live tests.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/servicebus/Azure.Messaging.ServiceBus/tests/Receiver/SessionReceiverTests.cs | Adds unit tests for default values and option validation scenarios. |
| sdk/servicebus/Azure.Messaging.ServiceBus/tests/Receiver/SessionReceiverLiveTests.cs | Adds gated live tests covering non-exclusive token assignment and cooperative takeover flows. |
| sdk/servicebus/Azure.Messaging.ServiceBus/tests/Receiver/ReceiverTests.cs | Updates Moq callback signature to match receiver creation signature changes. |
| sdk/servicebus/Azure.Messaging.ServiceBus/tests/Primitives/ServiceBusConnectionTests.cs | Updates test transport client override signature for new receiver parameters. |
| sdk/servicebus/Azure.Messaging.ServiceBus/tests/Amqp/AmqpConnectionScopeTests.cs | Adds unit tests verifying AMQP source-filter construction and token surfacing/backstop behavior. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.resx | Adds new resource strings for validation and unsupported-feature errors. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.Designer.cs | Regenerates resource accessors for new strings. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusSessionReceiverOptions.cs | Adds opt-in non-exclusive flag and takeover token; plumbs into internal receiver options. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusSessionReceiver.cs | Adds public SessionLockToken and validates option combinations for session acceptance. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiverOptions.cs | Adds internal carriers for session exclusivity and lock token to reach transport layer. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Receiver/ServiceBusReceiver.cs | Passes exclusivity/token through to transport receiver creation. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Primitives/ServiceBusConnection.cs | Extends transport receiver creation to pass exclusivity/token to the transport client. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Core/TransportReceiver.cs | Adds abstract SessionLockToken to transport receiver contract. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Core/TransportClient.cs | Extends receiver factory method signature to include exclusivity/token. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Amqp/AmqpReceiver.cs | Implements token surfacing/backstop and relaxes NotFound settlement guard for non-exclusive sessions. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Amqp/AmqpConnectionScope.cs | Wires AMQP source filters for session-exclusive-mode and session-lock-token. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Amqp/AmqpClientConstants.cs | Adds AMQP symbol constants for new filters/properties. |
| sdk/servicebus/Azure.Messaging.ServiceBus/src/Amqp/AmqpClient.cs | Threads exclusivity/token into AmqpReceiver construction. |
| sdk/servicebus/Azure.Messaging.ServiceBus/CHANGELOG.md | Documents the new opt-in non-exclusive session locking feature. |
| sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.netstandard2.0.cs | Updates public API listing for new properties. |
| sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.net8.0.cs | Updates public API listing for new properties. |
| sdk/servicebus/Azure.Messaging.ServiceBus/api/Azure.Messaging.ServiceBus.net10.0.cs | Updates public API listing for new properties. |
Files not reviewed (1)
- sdk/servicebus/Azure.Messaging.ServiceBus/src/Resources.Designer.cs: Generated file
…er params CreateTransportReceiver gained isSessionExclusive (bool) and sessionLockToken (Guid?) trailing params. GetMockConnection's Moq .Callback still used the 9-arg signature, which compiles but throws ArgumentException at runtime (Moq validates callback arity). Expand the callback to 11 type args + lambda params, mirroring ReceiverTests.
Copilot started reviewing on behalf of
Eldert Grootenboer (EldertGrootenboer)
June 19, 2026 19:56
View session
…sionFilter (match latest service design)
The broker supports acquiring the next available session non-exclusively (sessionId=null, non-exclusive). Remove the incorrect client-side validation that rejected AcceptNextSessionAsync with IsSessionExclusive=false, and drop the now-unused resource string and its unit test. A takeover lock token still requires a specific session.
…sions - Add AmqpNonExclusiveSessionFilterCodec encode/decode round-trip tests (session id + token, no token, accept-any null session id) - Add AcceptNextSessionAllowsNonExclusiveWithoutSessionId verifying accept-any is allowed in non-exclusive mode
vinaysurya
reviewed
Jul 8, 2026
Addresses review feedback: the codec carries meaningful fields (session id and lock token), so provide a ToString() that describes them for diagnostics. Adds a unit test covering the output.
Addresses review feedback: covers AcceptNextSessionAsync (the accept-any path with no session id) plus IsSessionExclusive=false, then a token-based takeover and cross-receiver settle. Gated with the existing NonExclusiveFeatureSkipReason like its siblings.
Copilot started reviewing on behalf of
Eldert Grootenboer (EldertGrootenboer)
July 8, 2026 17:55
View session
Addresses Copilot review feedback: the filter-construction tests use a single composite non-exclusive session filter (session id + lock token), not separate mode/token filters; update the doc comments to match. Reword the NotSupportedException message in terms of feature availability for the namespace rather than a namespace 'version'.
Copilot started reviewing on behalf of
Eldert Grootenboer (EldertGrootenboer)
July 8, 2026 18:08
View session
Addresses Copilot review feedback: the comment referenced only the plain session filter, but a non-exclusive session receiver adds the composite non-exclusive session filter instead. Reword to cover both branches.
This was referenced Sep 26, 2026
Bump the minor-and-patch-updates group with 6 updates
frasermolyneux/portal-servers-integration#1054
Merged
Merged
Merged
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds opt-in non-exclusive session locking to the Service Bus session
receiver, allowing a session that is normally held by a single receiver to be
cooperatively taken over by another receiver. The feature is opt-in and the
default behavior (exclusive session locks) is unchanged, so existing code is
unaffected.
Behavior
Two modes, selected by
ServiceBusSessionReceiverOptions.EnableNonExclusiveSession(default
false):for the lock duration, and a second accept on the same session fails as it
does today.
EnableNonExclusiveSessionset totrue,the receiver opts into cooperative locking via the AMQP source filter
com.microsoft:non-exclusive-session-filter, a composite filter carrying thesession id and, for a takeover, a lock token. The service echoes the filter on
the attach response with the assigned session lock token, surfaced read-only
as
ServiceBusSessionReceiver.SessionLockToken. A second receiver presentsthat token via
ServiceBusSessionReceiverOptions.SessionLockTokento takeover the session, then settles the original holder's in-flight messages over
the management link. The displaced holder's receive link is force-detached by
the service, so its next operation surfaces
SessionLockLostException.If the connected service does not support the feature, opting in fails loudly
with
NotSupportedExceptionrather than silently downgrading. Exclusivesessions never reach the new code paths.
The scope is
ServiceBusSessionReceiveronly.ServiceBusSessionProcessorhasno equivalent option and continues to lock sessions exclusively; that is
deliberate, is called out in the CHANGELOG entry, and processor support is
tracked internally.
API
The token is
Guid?on input andstringon output, matching the existingmessage
LockToken, which is likewise a service-providedstring.What changed
IsSessionExclusiveandSessionLockTokenon the receiver, and settableEnableNonExclusiveSessionandSessionLockTokenon the options. NewCHANGELOG.mdentry.non-exclusive-session-filter, carrying session id then lock token(
AmqpConnectionScope,AmqpClient,AmqpClientConstants,AmqpNonExclusiveSessionFilterCodec).AmqpReceiverrelaxes the receive-linkNotFoundsettlement guard for non-exclusive sessions so the new holder cansettle the previous holder's messages over the management link.
ServiceBusSessionReceiver(Options),ServiceBusReceiver(Options), and thetransport interfaces. New resource strings for the validation and
NotSupportedExceptionmessages.cancellationTokenmoved to the last parameter across theinternal signatures this touched, and the internal option carriers are now
get-only with an internal constructor so the shared default-options singleton
cannot be mutated.
Testing
non-exclusive mode, and requires a specific session id so it cannot be
combined with accepting the next available session), confirm that a
non-exclusive accept-next without a session id is allowed, and pin the wire
contract: the filter descriptor name, its code, and that the session id
encodes before the lock token, so a swapped field order fails rather than
round-tripping cleanly.
settlement. The five that depend on the service-side feature are
[Ignore]dwith a reason until the rollout reaches the test namespace, so the live
pipeline stays green; re-enabling them is tracked internally.
Azure.Messaging.ServiceBusunit suite passes (675 passed, 0 failed).dotnet formatis clean and the exported API listings regenerate unchanged.